﻿using LxBasic;

namespace LxCore
{
    public class EquationBase : MatrixBase
    {
        public IField<double> Source_b = ConstField.Double0;
        public IField<double> Source_a = ConstField.Double0;

        public EquationBoundBase[] EquationBounds;
        public override void CheckCreate(FvZone zone)
        {
            base.CheckCreate(zone);
            CheckCreator.ArrayStrict(ref EquationBounds, zone.Bounds.Count);
        }
        public void SetZone_CommonTerm(IField<double> source_b, IField<double> source_a, IField<double> iniValue, IParameter<double> relaxFactor)
        {
            this.Source_b = source_b;
            this.Source_a = source_a;
            this.IniValue = iniValue;
            this.RelaxFactor = relaxFactor;
        }
        public void SetBound_FixValueWall(IField<double> fixValue, FvBound thread)
        {
            CheckCreator.Instance(ref EquationBounds[thread.ID], out FixValueWallBound b);
            b.SetFixValue(fixValue);
        }
        public void SetBound_FixFluxWall(IField<double> fixFlux_b, IField<double> fixFlux_a, FvBound thread)
        {
            SetBound_FluxCombinedWall(fixFlux_b, fixFlux_a, ConstField.Double0, ConstField.Double0, thread);
        }
        public void SetBound_FixhTWall(IField<double> fixh, IField<double> fixT, FvBound thread)
        {
            SetBound_FluxCombinedWall(ConstField.Double0, ConstField.Double0, fixh, fixT, thread);
        }
        public void SetBound_FluxCombinedWall(IField<double> fixFlux_b, IField<double> fixFlux_a, IField<double> fixh, IField<double> fixT, FvBound thread)
        {
            CheckCreator.Instance(ref EquationBounds[thread.ID], out FluxWallBound b);
            b.SetFlux(thread, fixFlux_b, fixFlux_a, fixh, fixT);
        }
        public void SetBound_CoupledWall(IField<double> boundSource_b, IField<double> boundSource_a, FvBound thread)
        {
            CheckCreator.Instance(ref EquationBounds[thread.ID], out CoupledWallBound b);
            b.SetFlux(boundSource_b, boundSource_a);
        }
        public void SetBound_Symmetry(FvBound thread)
        {
            CheckCreator.Instance(ref EquationBounds[thread.ID], out FluxWallBound b);
            b.SetFlux(thread, ConstField.Double0, ConstField.Double0, ConstField.Double0, ConstField.Double0);
        }
        public override void Initialize()
        {
            if (!IsXInit)
            {
                MultiThreads.RunParallelTask((threadInfo) =>
                {
                    SetXs(IniValue, Zone.Inner, threadInfo);
                    for (int i1 = 0; i1 < EquationBounds.Length; i1++)
                    {
                        EquationBounds[i1].Initialize(this, Zone.Bounds[i1], threadInfo);
                    }
                });
                IsXInit = true;
            }
            if (!IsGradInit)
            {
                MultiThreads.RunParallelTask(CalGradient_LSQ);
                IsGradInit = true;
            }
        }
        public override void FormABOnCells(ThreadInfo threadInfo)
        {
            (int i1, int endp1) = Zone.Inner.CellIdx.Split(threadInfo);
            if (Source_a.IsEqual0())
            {
                if (Source_b.IsEqual0())
                {
                    for (; i1 < endp1; i1++)
                    {
                        ref FvInnCell cell = ref InnCells[i1];
                        ADiag[i1] = CalAdiag(ref cell);
                        B[i1] = cell.CalIfOwnerAddElseMunus(InnFaceShare, BouFaceShare);
                    }
                }
                else
                {
                    var sou_b = Source_b.GetCellVarArr(Zone.Inner);
                    for (; i1 < endp1; i1++)
                    {
                        ref FvInnCell cell = ref InnCells[i1];
                        ADiag[i1] = CalAdiag(ref cell);
                        B[i1] = cell.CalIfOwnerAddElseMunus(InnFaceShare, BouFaceShare) + sou_b.GetCellVar(i1) * cell.Volumn;
                    }
                }
            }
            else
            {
                var sou_b = Source_b.GetCellVarArr(Zone.Inner);
                var sou_a = Source_a.GetCellVarArr(Zone.Inner);
                for (; i1 < endp1; i1++)
                {
                    ref FvInnCell cell = ref InnCells[i1];
                    ADiag[i1] = CalAdiag(ref cell) - sou_a.GetCellVar(i1) * cell.Volumn;
                    B[i1] = cell.CalIfOwnerAddElseMunus(InnFaceShare, BouFaceShare) + sou_b.GetCellVar(i1) * cell.Volumn;
                }
            }
        }
        public override void UpdateBoundsValue(ThreadInfo threadInfo)
        {
            for (int i1 = 0; i1 < EquationBounds.Length; i1++)
            {
                switch (EquationBounds[i1])
                {
                    case FixValueWallBound fixValue: SetXs(fixValue.FixValue, Zone.Bounds[i1], threadInfo); break;
                    case FluxWallBound fluxbou: updateFluxBound(Zone.Bounds[i1], fluxbou, threadInfo); break;
                }
            }
        }
        protected void updateFluxBound(FvBound bound, FluxWallBound fluxBound, ThreadInfo threadInfo)
        {
            int start = bound.FaceIdx.Start;
            (int i1, int endp1) = bound.FaceIdx.Split(threadInfo);
            if (fluxBound.IsConstSource())
            {
                var factors = fluxBound.BouFactors1;
                for (; i1 < endp1; i1++)
                {
                    BouX[i1] = InnX[BouFaces[i1].OSideCell] + factors[i1 - start];
                }
            }
            else
            {
                var factors = fluxBound.BouFactors2;
                for (; i1 < endp1; i1++)
                {
                    ref var fac = ref factors[i1 - start];
                    BouX[i1] = fac.A * InnX[BouFaces[i1].OSideCell] + fac.B;
                }
            }
        }
        public virtual IConnectFactor GetConnectWallFactor(FvBound bound)
        {
            throw new NotImplementedException();
        }
        public class FactorCalcBase
        {
            public void SetBasic(EquationBase equation, FvBound bound)
            {
                this.BouCFaces = equation.BouFaces;
                this.Grad = equation.Grad;
                this.InnX = equation.InnX;
                this.BouX = equation.BouX;
                this.BouA = equation.ABouH;
                this.BouFaceShare = equation.BouFaceShare;
            }
            protected FvBouFace[] BouCFaces;
            protected Vector3d[] Grad;
            protected double[] InnX;
            protected double[] BouX;
            protected double[] BouA;
            protected double[] BouFaceShare;
        }
        public class EquationBoundBase : BoundHead
        {
            public virtual void Initialize(EquationBase inEquation, FvBound inBound, ThreadInfo threadInfo)
            {
                inEquation.SetXs(inEquation.IniValue, inBound, threadInfo);
            }
        }
        public class FixValueWallBound : EquationBoundBase
        {
            public IField<double> FixValue;
            public void SetFixValue(IField<double> fixValue)
            {
                this.FixValue = fixValue;
            }
            public override void Initialize(EquationBase inEquation, FvBound inBound, ThreadInfo threadInfo)
            {
                inEquation.SetXs(FixValue, inBound, threadInfo);
            }
        }
        public class FluxWallBound : EquationBoundBase
        {
            public IField<double> FixFlux_b;
            public IField<double> FixFlux_a;
            public IField<double> Fixh;
            public IField<double> FixT;
            public double[] BouFactors1;
            public (double A, double B)[] BouFactors2;
            public void SetFlux(FvBound bound, IField<double> fixFlux_b, IField<double> fixFlux_a, IField<double> fixh, IField<double> fixT)
            {
                this.FixFlux_b = fixFlux_b;
                this.FixFlux_a = fixFlux_a;
                this.Fixh = fixh;
                this.FixT = fixT;
                if (IsConstSource())
                {
                    CheckCreator.ArrayStrict(ref BouFactors1, bound.FaceIdx.GetCount());
                    BouFactors2 = null;
                }
                else
                {
                    BouFactors1 = null;
                    CheckCreator.ArrayStrict(ref BouFactors2, bound.FaceIdx.GetCount());
                }
            }
            public bool IsConstSource()
            {
                return Fixh.IsEqual0() && FixFlux_a.IsEqual0();
            }
        }
        public class CoupledWallBound : EquationBoundBase
        {
            public IField<double> FixFlux_b;
            public IField<double> FixFlux_a;
            public void SetFlux(IField<double> fixFlux_b, IField<double> fixFlux_a)
            {
                this.FixFlux_b = fixFlux_b;
                this.FixFlux_a = fixFlux_a;
            }
        }
    }
}